Skip to content

chore(assets-controllers): add isDeprecated to TokenDetectionController - #9362

Merged
Prithpal-Sooriya merged 8 commits into
mainfrom
cursor/deprecate-token-detection-controller-553e
Jul 21, 2026
Merged

chore(assets-controllers): add isDeprecated to TokenDetectionController#9362
Prithpal-Sooriya merged 8 commits into
mainfrom
cursor/deprecate-token-detection-controller-553e

Conversation

@Prithpal-Sooriya

@Prithpal-Sooriya Prithpal-Sooriya commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Explanation

As part of the assets-unify-state rollout, AssetsController supersedes legacy per-chain controllers. TokenDetectionController needs the same opt-out mechanism already added to TokenListController, TokensController, TokenBalancesController, and related controllers.

This PR adds an optional isDeprecated constructor callback to TokenDetectionController. When it returns true, the controller stops polling, blocks network activity, and becomes a no-op at every entry point (start, detectTokens, _executePoll, addDetectedTokensViaWs, and addDetectedTokensViaPolling). The callback is re-evaluated on each entry point so hosts can toggle deprecation at runtime without reconstructing the controller.

References

Manual testing steps

N/A — behavior-preserving when isDeprecated is omitted (defaults to false).

Screenshots/Recordings

N/A

Changed files

File Change Verification
packages/assets-controllers/src/TokenDetectionController.ts Added isDeprecated option and #enforceDisabledState() guards on all detection entry points yarn eslint on changed files
packages/assets-controllers/src/TokenDetectionController.test.ts Added isDeprecated test suite (9 cases) yarn workspace @metamask/assets-controllers run jest --no-coverage packages/assets-controllers/src/TokenDetectionController.test.ts -t "isDeprecated"
packages/assets-controllers/CHANGELOG.md Documented new isDeprecated option under Unreleased Pending PR number for link

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them
Open in Web Open in Cursor 

cursoragent and others added 2 commits July 2, 2026 08:06
Add an optional isDeprecated constructor callback so hosts can disable
token detection when AssetsController supersedes this controller via the
assets-unify-state feature flag. When deprecated, polling is stopped and
all detection entry points become no-ops without tearing down the
controller.

Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
…roller tests

Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
@Prithpal-Sooriya

Copy link
Copy Markdown
Contributor Author

@cursor get latest and resolve merge conflicts

@cursor

cursor Bot commented Jul 20, 2026

Copy link
Copy Markdown

Merged latest main into the PR branch and resolved the conflict.

Conflict: packages/assets-controllers/CHANGELOG.md — both branches had added entries under [Unreleased].

Resolution: Kept both:

  • Our isDeprecated entry for TokenDetectionController (#9362)
  • All new main Unreleased entries (Robinhood Chain, getAssetId, occurrence floor changes, etc.)

TokenDetectionController.test.ts auto-merged cleanly. All 9 isDeprecated tests still pass.

Pushed merge commit 97ddaa485 to the branch and left a comment on the PR. CI should re-run on the updated branch.

Open in Web Open in Cursor 

…553e

Resolve CHANGELOG.md conflict by keeping both the isDeprecated entry
and latest main Unreleased changes.

Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
@cursor

cursor Bot commented Jul 20, 2026

Copy link
Copy Markdown

Merged latest main and resolved the CHANGELOG.md conflict — kept both the isDeprecated entry and all new Unreleased changes from main. isDeprecated tests still pass locally.

Comment on lines +235 to +239
* @param options.isDeprecated - Optional function that returns true to completely
* disable this controller (no requests, no token detection). The function is
* evaluated dynamically on each entry point so it can be toggled at runtime.
* Intended for use when a higher-level controller (e.g. AssetsController)
* supersedes this one.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cursor keep this comment addition 1 short line, too long and want to keep diff short

@cursor cursor Bot Jul 20, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already done on the branch from the last review pass (e905f2de6).

The @param options.isDeprecated JSDoc is now a single line:

   * @param options.isDeprecated - Optional callback that disables token detection when it returns true.

The #enforceDisabledState JSDoc block was also removed. No further changes needed for this comment.

Open in Web Open in Cursor 

Comment on lines +311 to +317
/**
* Stops polling and blocks network activity so no token detection runs.
*
* Called from every entry point when `isDeprecated()` is true so that a
* runtime toggle propagates immediately, even if the controller was
* originally constructed while it was enabled.
*/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cursor remove comment, want to keep comment short.

@cursor cursor Bot Jul 20, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shortened the comments per review:

  • @param options.isDeprecated — reduced to one line: Optional callback that disables token detection when it returns true.
  • #enforceDisabledState — removed the JSDoc block entirely

Pushed in e905f2de6.

Open in Web Open in Cursor 

Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
Comment on lines +319 to +320
this.#stopPolling();
this.#disabled = true;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cursor you didn't follow the pattern, we are meant to return state, not this shit. basically we want to return an empty state.

Example other controller deprecation

  #enforceDisabledState(): void {
    if (
      Object.keys(this.state.accountsAssets).length === 0 &&
      Object.keys(this.state.assetsMetadata).length === 0 &&
      Object.keys(this.state.allIgnoredAssets).length === 0
    ) {
      return;
    }
    this.update((state) => {
      state.accountsAssets = {};
      state.assetsMetadata = {};
      state.allIgnoredAssets = {};
    });
  }

So we want to do this, and basically bail early.

@cursor cursor Bot Jul 20, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated #enforceDisabledState to match the shared deprecation pattern:

  #enforceDisabledState(): void {
    this.#stopPolling();
    if (Object.keys(this.state).length === 0) {
      return;
    }
    this.update(() => ({}));
  }

Changes:

  • Removed #disabled = true manipulation
  • Reset state via this.update() with an early return when already empty (for TokenDetectionController, state is always {})
  • Kept #stopPolling() so active intervals stop when deprecated
  • Entry points still call #enforceDisabledState() and bail early

Tests were updated to assert empty state / no side effects instead of isActive === false. All 8 isDeprecated tests pass.

Pushed in 059517e1f.

Open in Web Open in Cursor 

@cursor

cursor Bot commented Jul 20, 2026

Copy link
Copy Markdown

Updated #enforceDisabledState to follow the shared deprecation pattern — stops polling, resets state via this.update(() => ({})) with an early return when already empty, and bails at entry points without toggling #disabled. Tests updated accordingly (8 passing).

…rceDisabledState

Follow the shared isDeprecated pattern: clear state via this.update and
bail early at entry points instead of toggling the private disabled flag.

Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
@Prithpal-Sooriya
Prithpal-Sooriya marked this pull request as ready for review July 20, 2026 22:25
@Prithpal-Sooriya
Prithpal-Sooriya requested review from a team as code owners July 20, 2026 22:25

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 059517e. Configure here.

Comment thread packages/assets-controllers/src/TokenDetectionController.ts
Comment thread packages/assets-controllers/src/TokenDetectionController.ts
@Prithpal-Sooriya

Copy link
Copy Markdown
Contributor Author

@cursor check these comments.
#9362 (comment)
#9362 (comment)

My intention is we keep polling, but the state/update bails early (via the enforceDisabledState method).
So are we good?

@cursor

cursor Bot commented Jul 20, 2026

Copy link
Copy Markdown

We weren't quite aligned before — #enforceDisabledState was calling #stopPolling(), which stopped the legacy interval. That conflicts with your intent.

Your intent (correct pattern, same as TokenRatesController):

  • Polling keeps running
  • #enforceDisabledState only clears state via this.update()
  • Every entry point checks isDeprecated(), calls #enforceDisabledState(), and returns early — no detection work

Bugbot comments:

  1. "Deprecated start leaves polling scheduled" — fine under keep-polling. If deprecation flips mid-flight, the interval may still get scheduled, but later ticks are no-ops.
  2. "Deprecation skips mixin polling stop" — also fine. We intentionally don't call stopAllPolling() because polling should continue; _executePoll bails early like the other entry points.

Fix pushed (20b566769): removed #stopPolling() from #enforceDisabledState and updated the test to assert polling continues but getBalancesInSingleCall is never invoked after deprecation.

So yes — with that change, we're aligned with your intent.

Open in Web Open in Cursor 

…s deprecated

enforceDisabledState only clears state; entry points bail early without
stopping legacy or mixin polling, matching the shared isDeprecated pattern.

Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
@cursor

cursor Bot commented Jul 20, 2026

Copy link
Copy Markdown

Re: Bugbot comments — aligned with your intent now.

Your intent: keep polling running; #enforceDisabledState only clears state; entry points bail early.

Were we good before? Not quite — #enforceDisabledState was calling #stopPolling(), which stopped the legacy interval. That contradicted the keep-polling model.

Now:

  • #enforceDisabledState only does the state reset (this.update(() => ({})) with early return when already empty)
  • Entry points (start, detectTokens, _executePoll, addDetectedTokensViaWs, addDetectedTokensViaPolling) still check isDeprecated(), call #enforceDisabledState(), and return — no network/token work
  • Polling is not stopped on deprecation (legacy setInterval and mixin _executePoll can keep firing; each tick is a no-op)

Bugbot notes:

  1. "Deprecated start leaves polling scheduled" — acceptable under keep-polling; if deprecated flips during an in-flight detectTokens, the interval may still be scheduled, but subsequent ticks bail early.
  2. "Deprecation skips mixin polling stop" — intentional; we don't call stopAllPolling() because we want polling to continue.

Test updated: keeps polling but bails early when isDeprecated toggles to true at runtime.

juanmigdr
juanmigdr previously approved these changes Jul 21, 2026
@Prithpal-Sooriya
Prithpal-Sooriya added this pull request to the merge queue Jul 21, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Jul 21, 2026
@Prithpal-Sooriya

Copy link
Copy Markdown
Contributor Author

@cursor get latest and resolve conflicts

@cursor

cursor Bot commented Jul 21, 2026

Copy link
Copy Markdown

Merged latest main and resolved the conflict in packages/assets-controllers/CHANGELOG.md.

Resolution: Kept both Unreleased entries:

  • TokenDetectionController isDeprecated (#9362)
  • MultichainAssetsController isDeprecated (#9310)

Also updated our changelog wording to match the current behavior (keep polling, bail early at entry points — no longer says polling is stopped).

All 8 isDeprecated tests pass. Pushed merge commit 45e2a28ce.

Open in Web Open in Cursor 

@cursor

cursor Bot commented Jul 21, 2026

Copy link
Copy Markdown

Merged latest main and resolved the CHANGELOG.md conflict — kept both the TokenDetectionController (#9362) and MultichainAssetsController (#9310) isDeprecated entries. Also updated our changelog wording to reflect keep-polling / bail-early behavior. isDeprecated tests still pass.

…553e

Resolve CHANGELOG.md conflict by keeping both isDeprecated entries and
updating the TokenDetectionController note to reflect keep-polling behavior.

Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
@Prithpal-Sooriya
Prithpal-Sooriya added this pull request to the merge queue Jul 21, 2026
Merged via the queue into main with commit 921573f Jul 21, 2026
427 checks passed
@Prithpal-Sooriya
Prithpal-Sooriya deleted the cursor/deprecate-token-detection-controller-553e branch July 21, 2026 08:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants